#pragma once
#include "..\\Game.h"
#include "Camera.h"
#include "..\Bitmap Font\BitmapFont.h"
#include "..\Sprite Management\Animation.h"
#include "..\\Sprite Management\AnimationManager.h"
#include "..\\Particle Management\ParticleManager.h"
#include "..\\..\\SGD Wrappers\\SGD_GraphicsManager.h"
#include "..\\..\\SGD Wrappers\\SGD_Geometry.h"
#include "..\\Definitions.h"

#include <vector>
#include <cassert>

class Emitter;
class GameObject
{
private:
	SGD::Rectangle attackRect = SGD::Rectangle(-1.0f, -1.0f, -1.0f, -1.0f);
	char* cAnimation;		// Name of currently stored animation
	SGD::Point point;
	Animation* image = nullptr;
	SGD::Rectangle rect;
	SGD::Rectangle collisionRect;
	SGD::Size size;
	float fArea;
	int iObjectID;
	std::vector<Emitter*> vEmitters;

	/**********************************************************/
	// reference count
	unsigned int		m_unRefCount = 1;	// calling 'new' gives the prime reference

public:
	enum ObjectID { PLAYER = 0, MELEE, RANGED, POWERUP_HEALTH, POWERUP_SPEED, POWERUP_DAMAGE, POWERUP_EXTRA_LIFE, DESTRUCTABLE_OBJECT, CRUMBLING_PLATFORM, CHECK_POINT, WALL_TRIGGER_OBJECT, SWITCH_TRIGGER, SPIKE, SPRINGBOARD, PROJECTILE, LIQUID, BOSS, QUDZU, PAPABEAR, LOCKJAW };

	GameObject() = default;
	virtual ~GameObject()
	{
		for (unsigned int i = 0; i < vEmitters.size(); ++i)
		{
			delete vEmitters[i];
		}

		if (image != nullptr)
		{
			image->Clear();
			delete image;
		}
	}
	std::vector<Emitter*>& GetEmitters() { return vEmitters; }

	void AddEmitter(Emitter* emit){ vEmitters.push_back(emit); }

	virtual int Update(float dt)
	{ 
		if (GetAnimation() != nullptr)
			GetAnimation()->Update(dt);
		for (unsigned int i = 0; i < GetEmitters().size(); ++i)
		{
			GetEmitters()[i]->Update(dt);
			GetEmitters()[i]->SetPosition(GetPosition());
		}
		return 0;
	}
	virtual void Render()
	{
		Camera* camera = Camera::GetInstance();
		SGD::Point offset = GetPosition();
		offset.x -= camera->GetPosition().x;
		offset.y -= camera->GetPosition().y;
		if (IsOnCamera())
		{
			if (GetAnimation() != nullptr)
			{
				// Draw Image
				GetAnimation()->Render(offset);

				// Draw Collision Rect
				if (!GetCollisionRect().IsEmpty() && DEBUG)
					SGD::GraphicsManager::GetInstance()->DrawRectangle(GetCollisionRect(), { 0, 0, 0, 0 }, { 255, 0, 0, 255 });

				if (!GetAttackRect().IsEmpty() && DEBUG)
					SGD::GraphicsManager::GetInstance()->DrawRectangle(GetAttackRect(), { 0, 0, 0, 0 }, { 255, 255, 0, 0 });
			}

			// Render Emitters
			for (unsigned int i = 0; i < GetEmitters().size(); ++i)
				GetEmitters()[i]->Render();
		}
	}
	virtual void HandleCollision(const GameObject* collision){}
	virtual void HandleAttackCollision(const GameObject* collision){}

	// Accessors
	SGD::Rectangle GetAttackRect() const { return attackRect; }
	char* GetCurrentAnimationName()const { return cAnimation; }
	Animation* GetAnimation()const{ return image; }
	float GetTopCollisionOffset()const { return GetAnimation()->GetTopCollision(); }
	float GetRightCollisionOffset()const { return GetAnimation()->GetRightCollision(); }
	float GetLeftCollisionOffset()const { return GetAnimation()->GetLeftCollision(); }
	virtual SGD::Point GetPosition() const{ return point; }
	virtual int GetID()const { return iObjectID; }
	virtual SGD::Rectangle GetRect()const
	{
		SGD::Rectangle rect;
 		rect.left = point.x;
		rect.top = point.y;
		rect.right = rect.left + size.width;
		rect.bottom = rect.top + size.height;
		return rect;
	}
	virtual SGD::Size GetSize() const{ return size; }
	float GetArea()const { return fArea; }
	virtual SGD::Rectangle GetCollisionRect()const
	{ 
		SGD::Point offset = Camera::GetInstance()->GetPosition();
		offset.x = GetPosition().x - offset.x;
		offset.y = GetPosition().y - offset.y;
		return GetAnimation()->GetCollisionRect(offset);
	}

	//// Mutators
	void SetAttackRect(SGD::Rectangle rect) { attackRect = rect; }
	virtual void SetID(int id) { iObjectID = id; }
	virtual void SetAnimation(char* animation)
	{
		cAnimation = animation;
		if (image != nullptr)
			delete image;
		image = new Animation(GAME->GetAnimationManager()->GetAnimation(animation));
	}
	virtual void SetPosition(const SGD::Point pPoint){ point = pPoint; }
	virtual void SetSize(SGD::Size pSize)
	{
		size = pSize;
		float r = pSize.width * 0.5f;
		SetArea(2 * SGD::PI * r * pSize.height + 2 * SGD::PI * r * r);
	}
	virtual void SetCollisionRect(SGD::Rectangle rect) { collisionRect = rect; }
	void SetArea(float area) { fArea = area; }

	bool IsOnCamera()
	{
		SGD::Point offset = Camera::GetInstance()->GetPosition();
		if (GetPosition().x >= offset.x - 100
			&& GetPosition().y >= offset.y - 100
			&& GetPosition().x <= offset.x + 900
			&& GetPosition().y <= offset.y + 900)
			return true;
		return false;
	}


	virtual void	AddRef(void) final
	{
		assert(m_unRefCount != 0xFFFFFFFF && "Entity::AddRef - maximum reference count has been exceeded");

		++m_unRefCount;
	}
	virtual void	Release(void) final
	{
		--m_unRefCount;

		if (m_unRefCount == 0)
			delete this;
	}
};